//
//	(c) 2009 Andrew Kalmacky
//

#ifndef _AK_STRING_H_
#define _AK_STRING_H_

#include "../chars.h"
#include "../logger/logger.h"
#include "../ltm/ltm.h"
#include <vector>
#include "../ltm/ltm_vector.h"

namespace ak
{
	namespace string
	{
		using ak::ltm::pin;
		using ak::ltm::master;

		template<typename T>
		struct chunk
		{
			// source chunks should support:
			//static size_t size(const T& src);
			//static void render(char_t* target, const T& src);

			// chunks with raw addressing to chars should support:
			// static const const char_t* get_src_ptr(const T& dst);

			// target chunks should support:
			// static T make(size_t size);
			// static char_t* get_ptr(T& dst);
		};

		//
		// I need a language as efficient as C++ but without all the following crap!
		//

		template<typename DST, typename S1>
		DST combine(const S1& s1)
		{
			size_t size = chunk<S1>::size(s1);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			return r;
		}

		template<typename DST, typename S1, typename S2>
		DST combine(const S1& s1, const S2& s2)
		{
			size_t pos2 = chunk<S1>::size(s1);
			size_t size = pos2 + chunk<S2>::size(s2);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			chunk<S2>::render(d + pos2, s2);
			return r;
		}

		template<typename DST, typename S1, typename S2, typename S3>
		DST combine(const S1& s1, const S2& s2, const S3& s3)
		{
			size_t pos2 = chunk<S1>::size(s1);
			size_t pos3 = pos2 + chunk<S2>::size(s2);
			size_t size = pos3 + chunk<S3>::size(s3);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			chunk<S2>::render(d + pos2, s2);
			chunk<S3>::render(d + pos3, s3);
			return r;
		}

		template<typename DST, typename S1, typename S2, typename S3, typename S4>
		DST combine(const S1& s1, const S2& s2, const S3& s3, const S4& s4)
		{
			size_t pos2 = chunk<S1>::size(s1);
			size_t pos3 = pos2 + chunk<S2>::size(s2);
			size_t pos4 = pos3 + chunk<S3>::size(s3);
			size_t size = pos4 + chunk<S4>::size(s4);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			chunk<S2>::render(d + pos2, s2);
			chunk<S3>::render(d + pos3, s3);
			chunk<S4>::render(d + pos4, s4);
			return r;
		}

		template<typename DST, typename S1, typename S2, typename S3, typename S4, typename S5>
		DST combine(const S1& s1, const S2& s2, const S3& s3, const S4& s4, const S5& s5)
		{
			size_t pos2 = chunk<S1>::size(s1);
			size_t pos3 = pos2 + chunk<S2>::size(s2);
			size_t pos4 = pos3 + chunk<S3>::size(s3);
			size_t pos5 = pos4 + chunk<S4>::size(s4);
			size_t size = pos5 + chunk<S5>::size(s5);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			chunk<S2>::render(d + pos2, s2);
			chunk<S3>::render(d + pos3, s3);
			chunk<S4>::render(d + pos4, s4);
			chunk<S5>::render(d + pos5, s5);
			return r;
		}

		template<typename DST, typename S1, typename S2, typename S3, typename S4, typename S5, typename S6>
		DST combine(const S1& s1, const S2& s2, const S3& s3, const S4& s4, const S5& s5, const S6& s6)
		{
			size_t pos2 = chunk<S1>::size(s1);
			size_t pos3 = pos2 + chunk<S2>::size(s2);
			size_t pos4 = pos3 + chunk<S3>::size(s3);
			size_t pos5 = pos4 + chunk<S4>::size(s4);
			size_t pos6 = pos5 + chunk<S5>::size(s5);
			size_t size = pos6 + chunk<S6>::size(s6);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			chunk<S2>::render(d + pos2, s2);
			chunk<S3>::render(d + pos3, s3);
			chunk<S4>::render(d + pos4, s4);
			chunk<S5>::render(d + pos5, s5);
			chunk<S6>::render(d + pos6, s6);
			return r;
		}

		template<typename DST, typename S1, typename S2, typename S3, typename S4, typename S5, typename S6, typename S7>
		DST combine(const S1& s1, const S2& s2, const S3& s3, const S4& s4, const S5& s5, const S6& s6, const S7& s7)
		{
			size_t pos2 = chunk<S1>::size(s1);
			size_t pos3 = pos2 + chunk<S2>::size(s2);
			size_t pos4 = pos3 + chunk<S3>::size(s3);
			size_t pos5 = pos4 + chunk<S4>::size(s4);
			size_t pos6 = pos5 + chunk<S5>::size(s5);
			size_t pos7 = pos6 + chunk<S6>::size(s6);
			size_t size = pos7 + chunk<S7>::size(s7);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			chunk<S2>::render(d + pos2, s2);
			chunk<S3>::render(d + pos3, s3);
			chunk<S4>::render(d + pos4, s4);
			chunk<S5>::render(d + pos5, s5);
			chunk<S6>::render(d + pos6, s6);
			chunk<S7>::render(d + pos7, s7);
			return r;
		}

		template<typename DST, typename S1, typename S2, typename S3, typename S4, typename S5, typename S6, typename S7, typename S8>
		DST combine(const S1& s1, const S2& s2, const S3& s3, const S4& s4, const S5& s5, const S6& s6, const S7& s7, const S8& s8)
		{
			size_t pos2 = chunk<S1>::size(s1);
			size_t pos3 = pos2 + chunk<S2>::size(s2);
			size_t pos4 = pos3 + chunk<S3>::size(s3);
			size_t pos5 = pos4 + chunk<S4>::size(s4);
			size_t pos6 = pos5 + chunk<S5>::size(s5);
			size_t pos7 = pos6 + chunk<S6>::size(s6);
			size_t pos8 = pos7 + chunk<S7>::size(s7);
			size_t size = pos8 + chunk<S8>::size(s8);
			DST r = chunk<DST>::make(size);
			char_t* d = chunk<DST>::get_ptr(r);
			chunk<S1>::render(d, s1);
			chunk<S2>::render(d + pos2, s2);
			chunk<S3>::render(d + pos3, s3);
			chunk<S4>::render(d + pos4, s4);
			chunk<S5>::render(d + pos5, s5);
			chunk<S6>::render(d + pos6, s6);
			chunk<S7>::render(d + pos7, s7);
			chunk<S8>::render(d + pos8, s8);
			return r;
		}

		//
		// End of crap
		//

		template<>
		struct chunk<size_t>
		{
			enum { CAN_READ = 0, CAN_WRITE = 0 };
			static size_t size(size_t i)
			{
				size_t r = 1;
				for (; i >= 10; i /= 10)
					++r;
				return r;
			}
			static void render(char_t* at, size_t val)
			{
				at += size(val);
				for (;;)
				{
					*--at = T_('0') + val % 10;
					val /= 10;
					if (!val)
						break;
				}
			}
		};
		template<>
		struct chunk<int>
		{
			enum { CAN_READ = 1, CAN_WRITE = 0 };
			static size_t size(int i)
			{
				return i < 0 ? chunk<size_t>::size(-i) + 1 : chunk<size_t>::size(i);
			}
			static void render(char_t* at, int val)
			{
				if (val < 0)
				{
					val = -val;
					*at++ = T_('-');
				}
				chunk<size_t>::render(at, val);
			}
		};

		template<size_t N>
		struct chunk<const char_t[N]>
		{
			enum { CAN_READ = 1, CAN_WRITE = 0 };
			static size_t size(const char_t (&src)[N]) { return N - 1; }
			static void render(char_t* at, const char_t (&src)[N]) { memcpy(at, src, sizeof(char_t) * (N - 1)); }
			static const char_t* get_ptr(const char_t (&v)[N]) { return &v[0]; }
		};

		template<size_t N>
		struct chunk<char_t[N]>
		{
			enum { CAN_READ = 1, CAN_WRITE = 0 };
			static size_t size(const char_t (&src)[N]) { return N - 1; }
			static void render(char_t* at, const char_t (&src)[N]) { memcpy(at, src, sizeof(char_t) * (N - 1)); }
			static const char_t* get_ptr(const char_t (&v)[N]) { return &v[0]; }
		};

		template<>
		struct chunk< std::vector<char_t> >
		{
			enum { CAN_READ = 1, CAN_WRITE = 1 };
			static size_t size(const std::vector<char_t>& src) { return src.size(); }
			static void render(char_t* at, const std::vector<char_t>& src) { memcpy(at, &src[0], sizeof(char_t) * src.size()); }
			static std::vector<char_t> make(size_t size) { return std::vector<char_t>(size); }
			static const char_t* get_ptr(const std::vector<char_t>& v) { return &v[0]; }
			static char_t* get_ptr(std::vector<char_t>& v) { return &v[0]; }
		};

		template<>
		struct chunk< ak::ltm::vector<char_t> >
		{
			enum { CAN_READ = 1, CAN_WRITE = 1 };
			static size_t size(const ak::ltm::vector<char_t>& src) { return src.size(); }
			static void render(char_t* at, const ak::ltm::vector<char_t>& src) { memcpy(at, &src[0], sizeof(char_t) * src.size()); }
			static ak::ltm::vector<char_t> make(size_t size) { return ak::ltm::vector<char_t>(size); }
			static const char_t* get_ptr(const ak::ltm::vector<char_t>& v) { return &v[0]; }
			static char_t* get_ptr(ak::ltm::vector<char_t>& v) { return &v[0]; }
		};

		template<>
		struct chunk<char_t>
		{
			enum { CAN_READ = 1, CAN_WRITE = 0 };
			static size_t size(char_t src) { return 1; }
			static void render(char_t* at, char_t src) { *at = src; }
			static const char_t* get_ptr(const char_t& v) { return &v; }
		};

		template<>
		struct chunk<const char_t*>
		{
			enum { CAN_READ = 1, CAN_WRITE = 0 };
			static size_t size(const char_t* src) { return strlen(src); }
			static void render(char_t* at, const char_t* src)
			{
				while (*src)
					*at++ = *src++;
			}
			static const char_t* get_ptr(const char_t* v) { return v; }
		};

		struct substring_t
		{
			substring_t(const char_t *data, size_t length)
				: data(data)
				, length(length)
			{}
			const char_t* data;
			size_t length;
		};

		template<typename T>
		substring_t substring(const T& src, size_t from, size_t to)
		{
			size_t max = chunk<T>::size(src);
			if (from > max)
				from = max;
			if (to > max)
				to = max;
			if (from > to)
				from = to;
			return substring_t(chunk<T>::get_ptr(src) + from, to - from);
		}

		const size_t BAD_INDEX = ~0u;

		template<typename PATTERN, typename TEXT>
		size_t find(const PATTERN& pattern, const TEXT& in, size_t at = 0)
		{
			size_t pat_size = chunk<PATTERN>::size(pattern);
			const char_t* pat = chunk<PATTERN>::get_ptr(pattern);
			size_t in_size = chunk<TEXT>::size(in);
			const char_t* data = chunk<TEXT>::get_ptr(in);
			if (pat_size == 0)
				return at;
			for (size_t s = at; s <= in_size - pat_size + 1; s++)
			{
				if (data[s] != pat[0])
					continue;
				for (size_t i = 1;; i++)
				{
					if (i >= pat_size)
						return s;
					if (s + i > in_size || data[s + i] != pat[i])
						break;
				}
			}
			return BAD_INDEX;
		}

		class ConstCharBuffer: public ltm::SharedObject
		{
			friend class master_str;
			friend class temp_str;
		public:
			const char_t* c_str() const { return _data; }
			size_t size() const { return _size; }

			virtual void delete_this();
			static pin<ConstCharBuffer> make(size_t size);

		protected:
			ConstCharBuffer(size_t size)
				: _size(size)
			{
				_data[size] = 0;
			}

			// used in string::master_str()
			static pin<ConstCharBuffer> get_null();

			size_t _size;
			char_t _data[1];
		};

		class temp_str;

		class master_str: public master<ConstCharBuffer>
		{
		public:
			master_str() : master<ConstCharBuffer>(ConstCharBuffer::get_null()) {}
			master_str(const temp_str& src);
			master_str(const master<ConstCharBuffer>& src) : master<ConstCharBuffer>(src) {}
			master_str(const pin<ConstCharBuffer>& src) : master<ConstCharBuffer>(src) {}
			master_str(const ConstCharBuffer* src) : master<ConstCharBuffer>(src) {}

			size_t size() const no_throw { return (*this)->size(); }
			char_t operator[] (size_t i) const no_throw
			{
				return (*this)->c_str()[i];
			}
			const char_t* c_str() const no_throw { return (*this)->c_str(); }
			temp_str sub(size_t start_pos, size_t length) const;

			template<typename A1>
			master_str(const A1& a1)
				: master<ConstCharBuffer>(combine<master_str>(a1))
			{}
			template<typename A1, typename A2>
			master_str(const A1& a1, const A2& a2)
				: master<ConstCharBuffer>(combine<master_str>(a1, a2))
			{
			}
			template<typename A1, typename A2, typename A3>
			master_str(const A1& a1, const A2& a2, const A3& a3)
				: master<ConstCharBuffer>(combine<master_str>(a1, a2, a3))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4>
			master_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4)
				: master<ConstCharBuffer>(combine<master_str>(a1, a2, a3, a4))
			{}
			template<typename A1, typename A2, typename A3, typename A4, typename A5>
			master_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5)
				: master<ConstCharBuffer>(combine<master_str>(a1, a2, a3, a4, a5))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
			master_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6)
				: master<ConstCharBuffer>(combine<master_str>(a1, a2, a3, a4, a5, a6))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
			master_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7)
				: master<ConstCharBuffer>(combine<master_str>(a1, a2, a3, a4, a5, a6, a7))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
			master_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8)
				: master<ConstCharBuffer>(combine<master_str>(a1, a2, a3, a4, a5, a6, a7, a8))
			{
			}
		};

		class temp_str: public pin<ConstCharBuffer>
		{
		public:
			temp_str() : pin<ConstCharBuffer>(ConstCharBuffer::get_null()) {}
			temp_str(const pin<ConstCharBuffer>& src) : pin<ConstCharBuffer>(src) {}
			temp_str(const master_str& src) : pin<ConstCharBuffer>(src) {}

			size_t size() const no_throw { return (*this)->size(); }
			char_t operator[] (size_t i) const no_throw
			{
				return (*this)->c_str()[i];
			}
			const char_t* c_str() const no_throw { return (*this)->c_str(); }
			temp_str sub(size_t start_pos, size_t length) const
			{
				return combine<temp_str>(substring(*this, start_pos, start_pos + length));
			}

			template<typename A1>
			temp_str(const A1& a1)
				: pin<ConstCharBuffer>(combine<temp_str>(a1))
			{}
			template<typename A1, typename A2>
			temp_str(const A1& a1, const A2& a2)
				: pin<ConstCharBuffer>(combine<temp_str>(a1, a2))
			{
			}
			template<typename A1, typename A2, typename A3>
			temp_str(const A1& a1, const A2& a2, const A3& a3)
				: pin<ConstCharBuffer>(combine<temp_str>(a1, a2, a3))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4>
			temp_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4)
				: pin<ConstCharBuffer>(combine<temp_str>(a1, a2, a3, a4))
			{}
			template<typename A1, typename A2, typename A3, typename A4, typename A5>
			temp_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5)
				: pin<ConstCharBuffer>(combine<temp_str>(a1, a2, a3, a4, a5))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
			temp_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6)
				: pin<ConstCharBuffer>(combine<temp_str>(a1, a2, a3, a4, a5, a6))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
			temp_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7)
				: pin<ConstCharBuffer>(combine<temp_str>(a1, a2, a3, a4, a5, a6, a7))
			{
			}
			template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
			temp_str(const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, const A7& a7, const A8& a8)
				: pin<ConstCharBuffer>(combine<temp_str>(a1, a2, a3, a4, a5, a6, a7, a8))
			{
			}
		};

		inline temp_str master_str::sub(size_t start_pos, size_t length) const
		{
			return combine<temp_str>(substring(*this, start_pos, start_pos + length));
		}
		inline master_str::master_str(const temp_str& src)
			: master<ConstCharBuffer>(src)
		{
		}

		template<>
		struct chunk<master_str>
		{
			enum { CAN_READ = 1, CAN_WRITE = 1 };
			static size_t size(const master_str& src) { return src.size(); }
			static void render(char_t* at, const master_str& src)
			{
				memcpy(at, src.c_str(), src.size() * sizeof(char_t));
			}
			static temp_str make(size_t size) { return ConstCharBuffer::make(size); }
			static const char_t* get_ptr(const master_str& v) { return v.c_str(); }
			static char_t* get_ptr(master_str& v) { return const_cast<char_t*>(v.c_str()); }
		};

		template<>
		struct chunk<temp_str>
		{
			enum { CAN_READ = 1, CAN_WRITE = 1 };
			static size_t size(const temp_str& src) { return src.size(); }
			static void render(char_t* at, const temp_str& src) { memcpy(at, src.c_str(), src.size() * sizeof(char_t)); }
			static temp_str make(size_t size) { return ConstCharBuffer::make(size); }
			static const char_t* get_ptr(const temp_str& v) { return v.c_str(); }
			static char_t* get_ptr(temp_str& v) { return const_cast<char_t*>(v.c_str()); }
		};

		template<>
		struct chunk< pin<ConstCharBuffer> >
		{
			enum { CAN_READ = 1, CAN_WRITE = 1 };
			static size_t size(const pin<ConstCharBuffer>& src) { return src->size(); }
			static void render(char_t* at, const pin<ConstCharBuffer>& src) { memcpy(at, src->c_str(), src->size() * sizeof(char_t)); }
			static pin<ConstCharBuffer> make(size_t size) { return ConstCharBuffer::make(size); }
			static const char_t* get_ptr(const pin<ConstCharBuffer>& v) { return v->c_str(); }
			static char_t* get_ptr(pin<ConstCharBuffer>& v) { return const_cast<char_t*>(v->c_str()); }
		};

		template<>
		struct chunk<substring_t>
		{
			enum { CAN_READ = 1, CAN_WRITE = 0 };
			static size_t size(const substring_t& src) { return src.length; }
			static void render(char_t* at, const substring_t& src) { memcpy(at, src.data, src.length * sizeof(char_t)); }
			static const char_t* get_ptr(const substring_t& v) { return v.data; }
		};

		template<bool T, typename RES>
		struct IF_T
		{
		};
		template<typename RES>
		struct IF_T<true, RES>
		{
			typedef RES type;
		};

		template<typename A, typename B>
		typename IF_T<chunk<A>::CAN_READ && chunk<B>::CAN_READ, int>::type compare (const A& a, const B& b)
		{
			const char_t* aa = chunk<A>::get_ptr(a);
			const char_t* bb = chunk<B>::get_ptr(b);
			return strcmp(aa, bb);
		}

		template<typename A, typename B>
		typename IF_T<chunk<A>::CAN_READ && chunk<B>::CAN_READ, bool>::type operator== (const A& a, const B& b)
		{
			return compare(a, b) == 0;
		}
	}
}
namespace Log
{
	inline Stream &operator<< (Stream& s, const ak::string::temp_str& v)
	{
		return s << v.c_str();
	}
	inline Stream &operator<< (Stream& s, const ak::string::master_str& v)
	{
		return s << v.c_str();
	}
}

#endif // _AK_STRING_H_
